import logging
import math
import random
import wx

import pyciv.action
import pyciv.attribute
import pyciv.combat
import pyciv.dictionary
import pyciv.hexagon
import pyciv.player
import pyciv.resources
import pyciv.rules
import pyciv.state
import pyciv.terrain
import pyciv.unit

class GameOver(Exception):
    def __init__(self, message):
        self.message = message

class Engine(object):

    def __init__(self):
        self.state = pyciv.state.State(5)
        self.actions = []
        self.hex_action = pyciv.dictionary.WithDefault(self.state.valid_hex, pyciv.action.NONE)
        self.current_hex = None
        self.combat_results = [pyciv.combat.Result()]

    def create_default_state(self):
        self.state.players.append(pyciv.player.Human(wx.BLUE, "First"))
        self.state.players.append(pyciv.player.Human(wx.RED, "Second"))

        random.seed(13786)
        for hex in self.state.hexes:
            self.state.terrain[hex] = random.choice(pyciv.terrain.DISTRIBUTION)
            if hex[2] < -2:
                self.state.owner[hex] = self.state.players[1]
            elif hex[2] > 2:
                self.state.owner[hex] = self.state.players[2]

        self.state.unit[(-3,0,-3)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 3,
                                                               pyciv.attribute.PROTECTION: 2,
                                                               pyciv.attribute.FIREPOWER: 3})
        self.state.unit[(-1,2,-3)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 3,
                                                               pyciv.attribute.PROTECTION: 2,
                                                               pyciv.attribute.FIREPOWER: 3})
        self.state.unit[(1,4,-3)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 4,
                                                              pyciv.attribute.PROTECTION: 3,
                                                              pyciv.attribute.FIREPOWER: 4})
        self.state.unit[(-1,3,-4)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 2,
                                                               pyciv.attribute.PROTECTION: 4,
                                                               pyciv.attribute.FIREPOWER: 2})
        self.state.unit[(0,-3,3)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 3,
                                                              pyciv.attribute.PROTECTION: 3,
                                                              pyciv.attribute.FIREPOWER: 3})
        self.state.unit[(1,-2,3)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 3,
                                                              pyciv.attribute.PROTECTION: 3,
                                                              pyciv.attribute.FIREPOWER: 3})
        self.state.unit[(3,0,3)] = pyciv.unit.Unit("Kalle", {pyciv.attribute.MOBILITY: 3,
                                                             pyciv.attribute.PROTECTION: 3,
                                                             pyciv.attribute.FIREPOWER: 3})

        self.state.supply[(-3,2,-5)] = 50
        self.state.supply[(5,0,5)] = 50
        
        self.state.victory[(-3,-1,-2)] = 1
        self.state.victory[(-1,-3,2)] = 1
        self.state.victory[(4,5,-1)] = 1
        self.state.victory[(4,2,2)] = 1
        self.state.victory[(1,1,0)] = 1
    def end_turn(self):
        """End current player turn"""
        self._next_player()
        self.play_game()

    def play_game(self):
        while self.state.players[self.state.current_player].do_turn(self):
            self._next_player()

    def current_player(self):
        return self.state.players[self.state.current_player]

    def _next_player(self):
        # Check game over
        if not self.current_player().is_default_player():
            self._game_over()

        # Clear all selections
        self.deselect()
        
        # Perform operations AFTER player
        self.current_player().after_turn(self)

        self.state.current_player += 1
        if self.state.current_player == len(self.state.players):
            self.state.current_player = 0
            self.state.turn += 1

        # Perform operations BEFORE player
        self.current_player().before_turn(self)
    
    def _game_over(self):    
        points_to_win = self.points_to_win()
        if self.current_player().attributes[pyciv.attribute.VICTORY] >= points_to_win:
            raise GameOver("Won on points %d (needed %d)" % (self.current_player().attributes[pyciv.attribute.VICTORY], points_to_win))
        if self.state.turn == pyciv.rules.TURN_LIMIT:
            raise GameOver("Reached turn %d" % pyciv.rules.TURN_LIMIT)

    def current_player_owns(self, hex):
        return self.state.owner[hex] == self.current_player()

    def next_to_current_hex(self, hex):
        for neighbour in pyciv.hexagon.get_neighbours(self.current_hex):
            if hex == neighbour:
                return True
        return False

    def execute_action(self, index):
        self.actions[index].execute(self)
        self.deselect()
        self.calculate_intel()

    def generate_actions(self):
        if self.current_hex is not None:
            for hex in self.state.hexes:
                self.hex_action[hex] = self.state.unit[self.current_hex].generate_hex_action(hex, self)
            self.actions = self.state.unit[self.current_hex].generate_actions(self.current_hex, self)

    def select(self, hex):
        if self.current_hex is None:
            if self.state.owner[hex] == self.current_player():
                self.current_hex = hex
                self.generate_actions()
        else:
            self.hex_action[hex].execute(self)
            self.deselect()
            self.calculate_intel()
            self.select(hex)

    def deselect(self):
        self.current_hex = None
        self.hex_action.clear()
        self.actions = []
        
    def can_end_turn(self):
        result = [True, "End current turn"]
        return result

    def calculate_intel(self):
        # Clear all first
        for hex in self.state.hexes:
            self.state.intel[hex] = pyciv.intel.NONE

        # Then add what we know
        for hex in self.state.hexes:
            if self.state.owner[hex] == self.current_player():
                self.state.intel[hex] = pyciv.intel.FULL
                if self.state.unit[hex] is not pyciv.unit.NONE:
                    for neighbour in pyciv.hexagon.get_neighbours(hex):
                        self.state.intel[neighbour] = pyciv.intel.FULL

    def points_to_win(self):
        points = [player.attributes[pyciv.attribute.VICTORY] for player in self.state.players][1:]
        
        
        turn_score = 0
        for hex in self.state.hexes:
            turn_score += self.state.victory[hex]
        points_left = (pyciv.rules.TURN_LIMIT - self.state.turn + 1) * turn_score

        logging.info("Points: %s, Left: %d" % (points, points_left))
        return int(math.ceil(
                float(points[0] + points[1] + points_left) / 2))
